package mapper;

import anno.*;
import config.Configuration;
import org.apache.log4j.Logger;
import session.SqlSessionImpl;
import test.Test;
import sql.ConditionSqlNode;
import sql.WhereSqlNode;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MapperRegistry {
    private static final Logger logger = Logger.getLogger(Test.class);
    //保留mapper类和其动态代理
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap();
    //保留mapper类和其对应的model ,也就是数据库表对应的model
    private final Map<Class<?>, String> mapperModel = new HashMap<Class<?>, String>();
    private final Configuration configuration;

    public MapperRegistry(Configuration config) {
        this.configuration = config;
    }

    public Class<?> getMapperBindModel(Class<?> type) {
        String className = mapperModel.get(type);
        Class<?> modelClass = null;
        try {
            //获取类加载器，加载当前类
            modelClass = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
        return modelClass;
    }

    public <T> T getMapper(Class<T> type) {

        MapperProxyFactory mapperProxyFactory = knownMappers.get(type);
        //说明首次使用
        if (mapperProxyFactory == null) {
            //首次获取，添加到configuration中
            addMapperToConfiguration(type);
            mapperProxyFactory = knownMappers.get(type);
        }

        return (T) mapperProxyFactory.newInstance(new SqlSessionImpl(configuration));
    }

    public <T> boolean hasMapper(Class<T> type) {
        return knownMappers.containsKey(type);
    }

    public <T> void addMapper(Class<T> type) {
        MapperProxyFactory mapperProxyFactory = new MapperProxyFactory(type);
        //添加新的mapperProxyFactory
        knownMappers.put(type, mapperProxyFactory);
    }

    private void addMapperToConfiguration(Class<?> beanClass) {
        if (beanClass.isAnnotationPresent(ORMMapper.class)) {
            ORMMapper ormMapper = beanClass.getAnnotation(ORMMapper.class);
            //对应mapper绑定model的路径
            String modelpath = ormMapper.value();
            //加入到容器中，保留了beanClass 和其对应数据库的model
            mapperModel.put(beanClass, modelpath);
            System.out.println("MapperRegistry register " + beanClass);

            //添加mapper于注册器中
            configuration.getMapperRegistry().addMapper(beanClass);
            Method[] methods = beanClass.getMethods();
            for (Method method : methods) {
                MappedStatement mappedStatement = getMappedStatement(method);
                configuration.getMappedStatements().put(beanClass.getName() + "." + method.getName(), mappedStatement);
            }
        }
    }

    private MappedStatement getMappedStatement(Method method) {
        ORMSelect ormSelect = method.getAnnotation(ORMSelect.class);
        ORMInsert ormInsert = method.getAnnotation(ORMInsert.class);
        ORMUpdate ormUpdate = method.getAnnotation(ORMUpdate.class);
        ORMDelete ormDelete = method.getAnnotation(ORMDelete.class);

        MappedStatement mappedStatement = null;
        SqlCommandType sqlCommandType = SqlCommandType.UNKNOWN;
        String sql = null;

        if (ormSelect != null) {
            mappedStatement = new MappedStatement();
            sqlCommandType = SqlCommandType.SELECT;
            sql = ormSelect.value();
        }
        if (ormInsert != null) {
            mappedStatement = new MappedStatement();
            sqlCommandType = SqlCommandType.INSERT;
            sql = ormSelect.value();
        }
        if (ormUpdate != null) {
            mappedStatement = new MappedStatement();
            sqlCommandType = SqlCommandType.UPDATE;
            sql = ormSelect.value();
        }
        if (ormDelete != null) {
            mappedStatement = new MappedStatement();
            sqlCommandType = SqlCommandType.DELETE;
            sql = ormSelect.value();
        }
        if (mappedStatement != null) {
            mappedStatement.setSqlCommandType(sqlCommandType);
            // 如果注解中没有Where语句，则从@ORMParam参数注解中解析出条件参数，生成完整的SQL语句
            sql = buildWhereSQLIfNeeded(method, sql);

            mappedStatement.setResource(sql);
        }
        return mappedStatement;
    }

    private String buildWhereSQLIfNeeded(Method method, String sql) {
        Pattern pattern = Pattern.compile("\\b(where|Where)\\b");
        Matcher m = pattern.matcher(sql);
        boolean hasExistedWhere = m.find();
        if (!hasExistedWhere) {
            WhereSqlNode wsn = new WhereSqlNode(sql);
            Parameter[] parameters = method.getParameters();
            for (Parameter parameter : parameters) {
                ORMParam ormParam = parameter.getAnnotation(ORMParam.class);
                if (ormParam != null) {
                    ConditionSqlNode cqn = new ConditionSqlNode(ormParam.value() + " = ?");
                    wsn.addNode(cqn);
                }
            }
            sql = wsn.build();
            System.out.println("buildWhereSQLIfNeeded(), sql=" + sql);
        }
        return sql;
    }
}
